#include <iostream>
#include <vector>
#include <cstdio>
using namespace std;
#include "Lexer.h"
#include "Parser.h"

// Parser constructor
Parser::Parser()
{
	output.assign("");
	ResetError();
}

// Parser constructor
Parser::Parser(const char *newInput):lexer(newInput)
{
	output.assign("");
	ResetError();
}

// Initialize the expression to be parsed
void Parser::setInput(const char *newInput)
{
	lexer.setLexBuf(newInput);
	ResetError();
}

// Reset the error string to null
void Parser::ResetError()
{
	error.assign("");
}

// Set the error string. Called by the Parser when it encounters a syntax error.
void Parser::SetError(int expected, int got)
{
	if( error.compare("") )
		return;
		if ( ID == expected )
			error.assign("Error: expecting identifier, found ");
		else if ( CLOSE_PAREN == expected )
		error.assign ("Error: expecting close parenthesis, found ");
	else if ( DONE == expected )
		error.assign("Error: expecting binary operator ( &, |, -> ), found ");
	switch( got )
	{
		case IMPLIES:
			error.append("->");
		break;
		case ID:
			error.append("identifier");
		break;
		case DONE:
			error.append(".");
		break;
		default:
			error.append(1, got);
		break;
	}
}

// returns the error string
const char * Parser::GetError(void)
{
  return error.c_str();
}

// returns the ouput postfix expression
const char * Parser::getOutput(void)
{
	return output.c_str();
}

// returns the count of string successfully parsed up till now
int Parser::getParsedOutputCount(void)
{
	return parsedOutput.size();
}

// returns the postfix expression corresponding to index. 
// parsedOutput stores the postfix expression of all the expressions parsed successfully up till now.
const char * Parser::getParsedOutputAtIndex(int index)
{
	if( index >= 0 && index < parsedOutput.size() )
		return parsedOutput[index]->c_str();
	return NULL;
}

// Parse the input expression. Output is postorder expression. Using predictive parser.
int Parser::parse(void)
{
	output.assign("");
	// get the first token.
	lookahead = lexer.getLexan(&entryIndex);
	if(lookahead != DONE)
	{
		if (true == S())
		{
			if (DONE == lookahead)
			{
				// finished parsing this string.
				int iChar = 0, len = output.size();
				string *temp = new string("");
				temp->assign(output);
				parsedOutput.push_back(temp); 
				return SUCCESS;
			}
			else
				// wrong termination.
				SetError(DONE, lookahead);
		}
	}
	else
		error.assign("Error: null input");
	return FAILURE;
}

bool Parser::S(void)
{
	if (true == T())
	{
		if( lookahead == IMPLIES )
		{
			match(lookahead);
			if(true == S())
			{
				emit(entryIndex, IMPLIES);
				return true;
			}
		}
		else
			return true;
	}
	return false;
}

bool Parser::T(void)
{
	if (true == U())
	{
		if( lookahead == OR )
		{
			match(lookahead);
			if( true == T())
			{
				emit(entryIndex, OR);
				return true;
			}
		}
		else
			return true;
	}
	return false;
}

bool Parser::U(void)
{
	if ( true == V())
	{
		if( lookahead == AND )
		{
			match(lookahead);
			if (true == U())
			{
				emit(entryIndex, AND);
				return true;
			}
		}
		else
			return true;
	}
	return false;
}

bool Parser::V(void)
{
	switch (lookahead)
	{
		// found open parenthesis.
		case OPEN_PAREN:
			match(OPEN_PAREN);
			if (true == S())
			{
				if (true == match(CLOSE_PAREN))
					return true;
				else
				{
					cout << "Entered" << endl;
					SetError(CLOSE_PAREN, lookahead);
					return false;
				}
			}
			break;
			
		// found Not unary operator.
		case NOT:
			match(NOT);
			if (true == V())
			{
				emit(entryIndex, NOT);
				return true;
			}
			break;
			
		// found identifier.
		case ID:
			match(ID);
			emit(entryIndex, ID);
			return true;
			break;
	}
	SetError(ID, lookahead);
	return false;
}

// match the token with the symbol pointed by lookahead.
bool Parser::match(int token)
{
	if(lookahead == token)
	{
		lookahead = lexer.getLexan(&entryIndex);
		return true;
	}
	else
		return false;
}
// Ouput the token.
void Parser::emit(int entryIndex, int token)
{
	switch(token)
	{
		case AND:
			output.append("&.");
		break;
		case OR:
			output.append("|.");
		break;
		case NOT:
			output.append("!.");
		break;
		case IMPLIES:
			output.append("->.");
		break;
		case ID:
			char intStr[MAX_STRING_LEN];
			sprintf(intStr, "%d", entryIndex);
			output.append(intStr);
			output.append(".");
		break;
	}
}
